home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-22 | 6.7 KB | 161 lines | [TEXT/MPS ] |
- Multiple documents has now gotten a lot easier, thanks to the OpenDoc utility
- PartMaker™. It is a convenient way to start a new Part Editor for OpenDoc,
- but really it is a generic tool, so I've gone ahead and decided to use it for
- AppsToGo, as well. (Why not? PartMaker was written using AppsToGo...)
-
- First off, if you don't want to, you don't have to have a different source file
- for a different document. The functions in the source file Window.c will be
- called by default for any document that is created. In each function, where
- appropriate, you could simply test the OSType of the document the function is
- called to manage, and then execute the appropriate code based on this OSType.
-
- For example, the Window.c function ContentClick would be called by default for
- mouse clicks in the content of a window. You could do something like the below
- to determine what document type you are handling when this is called, and then
- do the appropriate thing for that document type.
-
- void ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
- {
- #ifndef __MWERKS__
- #pragma unused (firstClick)
- #endif
-
- ControlHandle ctl;
- short action, cnum;
- FileRecHndl frHndl;
-
- cnum = IsCtlEvent(window, event, &ctl, &action);
- /* That was easy. Scrolling was just handled. Other stuff would be handled
- ** by IsCtlEvent, if we had other stuff to do. We don't have any other
- ** controls in the content besides the document scrollbars. */
-
- frHndl = (FileRecHndl)GetWRefCon(window);
- switch ((*frHndl)->fileState.sfType) {
- case 'doc1':
- /* do "stuff" appropriate for 'doc1' based on the cnum returned. */
- break;
- case 'doc2':
- /* do "stuff" appropriate for 'doc2' based on the cnum returned. */
- break;
- }
-
- return;
- }
-
-
- The above code isn't all that ugly. If you have just two document types,
- then this may be what you want to do. However, it isn't too hard to see that
- if you have many document types, then the above case statements are just going
- to grow and grow. Also, if each document type has a lot of buttons or content
- processing, then each individual case statement will be complex, as well. This
- isn't very object-oriented at all.
-
- The new and better way to handle this problem is with the three PartMaker documents
- that are now included with AppsToGo. They are:
-
- 1) AWNewDialog
- 2) AWNewDoc
- 3) AWNewPalette
-
- The above PartMaker documents, along with a copy of PartMaker 2.2, can be found in
- the folder AppWannabeNewDocs.
-
- Let's say that for a while you assumed that your application would only have one
- palette. You just dropped your code into the stub functions in the file
- WindowPalette.c in your project, and everything worked just fine. Now you realize
- that your project needs another palette. You don't want to start adding case
- statements into code that already works, so therefore you want the code for the
- new palette in a file other than WindowPalette.c. No problem. Just double-click
- AWNewPalette, type in a name, and click "Create Palette". PartMaker will create a
- folder that contains two files.
-
- One of the files will be a source file that you will want to add to your project.
- The other file will contain step-by-step instructions as to what else you will need
- to do to instantiate your new palette. (The code changes that it lists will be to
- the file File.c, for example.)
-
- Now when this palette is instianted, the functions in the new source file will be
- called, instead of the funcitons in the file WindowPalette.c. Now that's reasonably
- object-oriented, and we're still in C.
-
-
-
- Another issue is that different documents may be related. If you close
- a document, you may also need to close other documents that were created in
- relation to that document. You will have to handle these issues yourself, although
- it isn't hard. You are messaged as to when a window/document is being closed. If
- that window has alternate views that should be closed at the same time, just search
- the window list for documents of that type, and close them right then.
-
- Yes, it is okay to close them recursively. The AppsToGo editor uses this very
- technique. Just remember that when you close a window, the window list shrinks
- in length, so walking the window list as it shrinks can get a bit tricky. What
- I do is if I close a window, I just start over at the beginning of the window list.
-
-
-
- There are interator functions in the framework that conveniently allow you to
- find the next document of a certain type. These iterators walk the window list
- looking for the next window that has a document of a certain type. This means
- that you can use these iterators only for documents that have a window. Just use
- this iterator to search for the next window that needs to get closed in relation
- to the window being closed, searching from the beginning of the list. The overhead
- of looking through the entire list is absolutely nothing as compared to the actual
- work involved in closing a window.
-
-
-
- If you are a heavy user of the hierarchical document architecture, you may end
- up with two different root object definitions for two different document types.
- It is convenient to store some non-undoable state data in the root object, but
- if two different documents have different state data, having only one root
- object type can get confusing. Due to this, you may find it useful to sub-type
- the root object in these instances.
-
- The standard root object definitions looks like this:
-
- long TRootObj(TreeObjHndl hndl, short message, long data);
- typedef struct {
- TreeObjHndl undo; /* This structure may be added to, but */
- FileRecHndl frHndl; /* these two first fields must remain. */
- } RootObj;
-
- You may wish to extend this to be something like the following:
-
- long TRootObj(TreeObjHndl hndl, short message, long data);
- typedef struct {
- TreeObjHndl undo; /* This structure may be added to, but */
- FileRecHndl frHndl; /* these two first fields must remain. */
- short subtype; /* Identifier for variant of root object. */
- union {
- Root1 r1; /* Union in the various root definitions here. */
- Root2 r2;
- } st;
- } RootObj;
-
-
- The code for the root object that is called by the framework would
- look something like:
-
- long TRootObj(TreeObjHndl hndl, short message, long data)
- {
- TreeObjProcPtr proc;
-
- if (proc = gRootMethods[mDerefRoot(hndl)->subtype])
- return((*proc)(hndl, message, data);
-
- return(0);
- }
-
- This code is dispatched to for a root object of whatever subtype. It in
- turn then dispatches to the code for the subtype of the root object. A
- table of procedures called gRootMethods is assumed here. It would be
- constructed similarly to the gTreeObjMethods table found in the file “File.c”.
-
-
- The rest is simply a matter of writing code. You will probably want to
- reference the various samples that use the framework to see how certain
- issues were addressed.
-
-
-